/*
 * TemplateFinder.java
 *
 * Created on September 25, 2007, 10:41 AM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package org.atomojo.www.util.script;

import org.atomojo.www.util.*;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Properties;
import java.util.logging.Level;
import org.restlet.Application;
import org.restlet.Context;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.data.MediaType;
import org.restlet.data.Metadata;
import org.restlet.resource.Finder;
import org.restlet.resource.ServerResource;
import org.restlet.routing.Template;

/**
 *
 * @author alex
 */
public class ScriptFinder extends Finder
{
   
   public static String RESOURCE_CONFIG_ATTR = "org.atomojo.www.script.config";
   public static String SCRIPT_CACHE_ATTR = "org.atomojo.www.script.cache";
   public static String RESOURCE_ATTR = "org.atomojo.www.resource";

   Class baseClass;
   String packageName;
   ResourceConfiguration resourceConfig;
   ScriptCache scriptCache;
   
   /** Creates a new instance of TemplateFinder */
   public ScriptFinder(Context context,Class baseClass)
   {
      this(context,baseClass,null);
   }
   /** Creates a new instance of TemplateFinder */
   public ScriptFinder(Context context,Class baseClass,String path)
   {
      super(context);
      this.baseClass = baseClass;
      this.packageName = path==null ? "/"+baseClass.getPackage().getName().replace('.','/')+"/" : 
                                      path.length()>0 && path.charAt(0)=='/' ? path : 
                                                                               "/"+baseClass.getPackage().getName().replace('.','/')+"/"+path;
      if (!this.packageName.endsWith("/")) {
         this.packageName += "/";
      }
      for (String name : getContext().getParameters().getNames()) {
         getLogger().info("Script parameter: "+name+" -> "+getContext().getParameters().getFirstValue(name));
      }
      //getContext().getAttributes().putAll(context.getAttributes());
      //getContext().setParameters(context.getParameters());
      resourceConfig = (ResourceConfiguration)context.getAttributes().get(RESOURCE_CONFIG_ATTR);
      if (resourceConfig==null) {
         getLogger().warning("Defaulting resource configuration.");
         resourceConfig = new ResourceConfiguration();
      }
      scriptCache = (ScriptCache)context.getAttributes().get(SCRIPT_CACHE_ATTR);
      if (scriptCache==null) {
         scriptCache = new ScriptCache();
      }
      
   }
   
   public ResourceConfiguration getResourceConfiguraiton() {
      return resourceConfig;
   }
   
   public ServerResource find(Request request,Response response)
   {
      //String path = packageName+request.getResourceRef().getRemainingPart();
      String path = request.getResourceRef().getRemainingPart();
      URL script = null;
      URI resource = (URI)request.getAttributes().get(RESOURCE_ATTR);
      int extPos = path.lastIndexOf('.');
      Application app = this.getApplication();
      Properties resourceProperties = resourceConfig.match(path);
      String stype = resourceProperties.getProperty("content-type");
      MediaType type = stype==null ? null : MediaType.valueOf(stype); 
      String defaultExtension = resourceProperties.getProperty("extension");
      if (defaultExtension==null) {
         defaultExtension = ".ats";
      }
      String agentsValue = resourceProperties.getProperty("agents");
      
      String resourceTemplate = resourceProperties.getProperty("resource");
      getLogger().info("resource template: "+resourceTemplate);
      if (resource==null && resourceTemplate!=null) {
         String href = null;
         String uri = null;
         try {
            Template t = new Template(resourceTemplate);
            href = t.format(request.getAttributes());
            String baseURISpec = resourceProperties.getProperty("base-uri");
            URI baseURI = null;
            if (baseURISpec!=null) {
               Template tbase = new Template(baseURISpec);
               uri = tbase.format(request.getAttributes());
               baseURI = new URI(uri);
            }

            resource = baseURI==null ? new URI(href) : baseURI.resolve(href);
         } catch (URISyntaxException ex) {
            getLogger().log(Level.SEVERE,"Cannot construct resource URI, href="+href+", base="+uri,ex);
            return null;
         }
      }
      
      String [] agents = null;
      if (agentsValue!=null) {
         agents = agentsValue.split(",");
      }
      
      MediaType forceType = null;

      if (type==null && extPos>=0) {
         String ext = path.substring(extPos+1);
         Metadata mdata = this.getApplication().getMetadataService().getMetadata(ext);
         if (mdata!=null) {
            type = MediaType.valueOf(mdata.getName());
         }
      } else if (path.length()==0 || path.charAt(path.length()-1)=='/') {
         // no extension and is a directory path, default to index.xml template
         script = baseClass.getResource(packageName+path+"index.ats");
         if (script==null) {
            script = baseClass.getResource(packageName+path+"index.xsl");
         }
         if (resource==null) {
            try {
               URL url = baseClass.getResource(packageName+path + "index.xml");
               /*
               if (url==null) {
                  getLogger().warning("Canot find resource via class path: "+path+"index.xml");
                  return null;
               }
               resource = url.toURI();
                */
               if (url!=null) {
                  resource = url.toURI();
               }
            } catch (URISyntaxException ex) {
               getLogger().log(Level.SEVERE,"Cannot convert URL to URI: "+path,ex);
               return null;
            }
         }
         if (type==null && agents!=null) {
            String agent = request.getClientInfo().getAgent();
            for (int i=0; i<agents.length; i++) {
               String exp = resourceProperties.getProperty(agents[i]+".match");
               if (agent.matches(exp)) {
                  if (getLogger().isLoggable(Level.FINE)) {
                     getLogger().fine("Matched agent "+agent+" with "+agents[i]);
                  }
                  stype = resourceProperties.getProperty(agents[i]+".content-type");
                  forceType = stype==null ? null : MediaType.valueOf(stype); 
                  break;
               }
            }
         }
         if (type==null) {
            type = MediaType.APPLICATION_XHTML_XML;
         }
      } else {
         // no extension, so lookup based on xml file
         script = baseClass.getResource(packageName+path+defaultExtension);
         if (script==null) {
            script = baseClass.getResource(packageName+path+".xsl");
         }
         if (resource==null) {
            try {
               URL url = baseClass.getResource(packageName+path + ".xml");
               /*
               if (url==null) {
                  getLogger().warning("Canot find resource via class path: "+path+".xml");
                  return null;
               }
               resource = url.toURI();
                */
               if (url!=null) {
                  resource = url.toURI();
               }
            } catch (URISyntaxException ex) {
               getLogger().log(Level.SEVERE,"Cannot convert URL to URI: "+path);
               return null;
            }
         }
         if (type==null && agents!=null) {
            String agent = request.getClientInfo().getAgent();
            for (int i=0; i<agents.length; i++) {
               String exp = resourceProperties.getProperty(agents[i]+".match");
               if (agent.matches(exp)) {
                  stype = resourceProperties.getProperty(agents[i]+".content-type");
                  forceType = stype==null ? null : MediaType.valueOf(stype); 
                  break;
               }
            }
         }
         if (type==null) {
            type = MediaType.APPLICATION_XHTML_XML;
         }
      }
      if (type==null) {
         type = app.getMetadataService().getDefaultMediaType();
      }
      boolean isResource = (type.getMainType().equals("image") || (script==null && type.getMainType().equals("text")) || (type.getMainType().equals("application") && !type.getName().equals("application/xml") && !type.getName().endsWith("+xml")));
      String sflag = resourceProperties.getProperty("force-resource");
      if (sflag!=null && sflag.equals("true")) {
         isResource = true;
      }
      sflag = resourceProperties.getProperty("force-script");
      if (sflag!=null && sflag.equals("true")) {
         isResource = false;
      }
      if (isResource) {
         if (getLogger().isLoggable(Level.FINE)) {
            getLogger().fine("Path is a resource: "+path+" "+type.getName());
         }
         path = packageName+path;
         return new ClassResource(baseClass,path);
      } else {
         if (forceType!=null) {
            type = forceType;
         }
         if (script==null) {
            String uri = resourceProperties.getProperty("script");
            if (uri!=null) {
               Template t = new Template(uri);
               String formatted = t.format(request.getAttributes());
               try {
                  script = new URL(formatted);
               } catch (MalformedURLException ex) {
                  getLogger().log(Level.SEVERE,"Cannot convert script to URL: "+uri,ex);
                  return null;
               }
            }
         }
         if (script==null) {
            getLogger().warning("Cannot location script for path "+path);
            return null;
         }
         try {
            URI uri = script.toURI();
            if (getLogger().isLoggable(Level.FINE)) {
               getLogger().fine("Using script "+uri);
            }
            if (!scriptCache.hasScript(uri)) {
               getLogger().info("adding "+uri);
               scriptCache.add(uri);
            }
            ScriptResource scriptResource = new ScriptResource(scriptCache,uri,resource,type);
            String username = resourceProperties.getProperty("resource.username");
            if (username!=null) {
               scriptResource.setResourceUser(username,resourceProperties.getProperty("resource.password"));
            }
            return scriptResource;
         } catch (URISyntaxException ex) {
            getLogger().log(Level.SEVERE,"Cannot convert URL to URI: "+path);
            return null;
         }
      }
   }
}
